/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email &
 * please set dead value or install git & please set dead value or install git
 * @Date: 2024-11-03 11:04:07
 * @LastEditors: yoola youlikesoilove@qq.com
 * user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2025-01-11 00:33:36
 * @FilePath: \CrabSystemProject\src\photoFunc\takePhoto.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置:
 * https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <iostream>
#include <string>
#include <thread>
#include <condition_variable>
#include <atomic>
#include <chrono>
#include <cstring>  // for std::strerror
#include <errno.h>  // for errno
#include <fcntl.h>  // for open

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgcodecs.hpp>

#include <ctime>
#include <sstream>
#include <iomanip>
#include <memory>
#include <filesystem>  // for C++17

#ifdef _WIN32
#include <windows.h>
const char* pipe_path = R"(\\.\pipe\capture_pipe)";
#else
#include <sys/stat.h>  // for mkfifo
#include <unistd.h>    // POSIX API函数，如 unlink、read 和 close
#include <fcntl.h>     // 如果有open、O_RDONLY 等使用
#include <sys/types.h>
#include <sys/stat.h>
#include <csignal>  // for signal handling
const char* pipe_path = "/tmp/capture_pipe";
#endif

#define DISABLE_DIRECT_OUTPUT "关闭默认输出，使用日志管理器"
#ifndef DISABLE_DIRECT_OUTPUT
#include <chrono>
#include <iomanip>
#include <sstream>
#define GET_LOCAL_TIME                                                                                      \
    []() {                                                                                                  \
        auto now = std::chrono::system_clock::now();                                                        \
        auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;     \
        std::time_t t = std::chrono::system_clock::to_time_t(now);                                          \
        std::tm tm = *std::localtime(&t);                                                                   \
        std::ostringstream oss;                                                                             \
        oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S.") << std::setw(3) << std::setfill('0') << ms.count(); \
        return oss.str();                                                                                   \
    }()
#define FILE_LINE   " - " << __FILE__ << "[" << __LINE__ << "] -  "
#define OUTPUT_ERRR std::cerr << GET_LOCAL_TIME << FILE_LINE
#define OUPUT_INFO  std::cout << GET_LOCAL_TIME << FILE_LINE
#else
#include "simpleLoger.hpp"
#endif
#define pic_dir "/home/pi/app/pic/"
std::condition_variable cv_condition;
std::mutex mtx;
std::atomic<bool> ready(false);
std::atomic<bool> running(true);   // 用于控制线程运行状态
std::atomic<bool> exiting(false);  // 用于控制线程运行状态

void write_to_pipe_exit(const char* pipe_path) {
    exiting = true;
    int w_fd = open(pipe_path, O_WRONLY | O_NONBLOCK);  // 非阻塞模式
    if (w_fd == -1) {
        OUTPUT_ERRR << "Failed to open write pipe! " << std::strerror(errno) << std::endl;
        exiting = false;
        return;
    }

    const char* exit_message = "exit";
    if (write(w_fd, exit_message, strlen(exit_message)) == -1) {
        OUTPUT_ERRR << "Failed to write exit command to pipe!" << std::endl;
        exiting = false;
    }
    close(w_fd);  // 关闭写入管道
}

#ifdef _WIN32
BOOL WINAPI ConsoleHandler(DWORD signal) {
    if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT) {
        OUPUT_INFO << "get exit signal!" << std::endl;
        running = false;  // 设置为停止运行状态
        cv_condition.notify_one();
        return true;
    }
    return false;
}
#else
void signal_handler(int signal) {
    OUPUT_INFO << "get exit signal!" << std::endl;
    running = false;  // 设置为停止运行状态
    cv_condition.notify_one();
    if (exiting != true) {
        std::thread write_thread(write_to_pipe_exit, pipe_path);
        // 分离线程，使它在后台独立运行
        write_thread.detach();
    } else {
        OUPUT_INFO << "有线程正在处理退出任务！" << std::endl;
    }
}
#endif

std::string getPicFilePath() {
    auto now = std::chrono::system_clock::now();
    auto t = std::chrono::system_clock::to_time_t(now);
    std::tm tm = *std::localtime(&t);

    std::ostringstream oss;
    // 创建以日期为目录名称的路径
    oss << pic_dir << std::put_time(&tm, "%Y-%m-%d") << "/";
    if (!std::filesystem::exists(oss.str())) {
        // 如果目录不存在，则创建
        if (std::filesystem::create_directory(oss.str())) {
            OUPUT_INFO << "成功创建目录: " << oss.str() << std::endl;
        } else {
            OUTPUT_ERRR << "创建目录失败: " << oss.str() << std::endl;
        }
    }
    return oss.str();
}

std::string getCurrentTimeString() {
    // 获取当前时间
    auto now = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;

    // 格式化时间字符串
    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y%m%d_%H%M%S");
    ss << '_' << std::setfill('0') << std::setw(3) << ms.count();

    return ss.str();
}

// std::string capture_photo() {
//     // 模拟拍照逻辑
//    OUPUT_INFO << "Capturing photo..." << std::endl;
//     std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟拍照耗时
//     std::string pic_path = "pic.jpg";
//     return pic_path;
//     // 拍照完成后将结果返回
// }

class CAPTURE {
private:
    cv::VideoCapture capture;
    std::string video_device;

public:
    std::string get_A_Pic(std::string);
    bool init();
    CAPTURE(std::string videoName) : video_device(videoName) {};
};

bool CAPTURE::init() {
    capture.open(video_device.c_str(), cv::CAP_V4L2);
    if (!capture.isOpened()) {
        OUTPUT_ERRR << "无法打开摄像头" << std::endl;
        return false;
    }

    // 设置分辨率为 1920x1080，格式为 MJPG
    capture.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
    capture.set(cv::CAP_PROP_FRAME_HEIGHT, 1080);
    capture.set(cv::CAP_PROP_FOURCC, cv::VideoWriter::fourcc('M', 'J', 'P', 'G'));  // 设置 MJPG 格式
    // 关键：设置V4L2缓冲区大小为1，这样每次读取都是最新帧
    capture.set(cv::CAP_PROP_BUFFERSIZE, 1);
    // 拍张照片但是不储存，检查下设备是否正常
    cv::Mat frame;
    if (!capture.read(frame)) {  // 检查是否成功捕获图像
        OUTPUT_ERRR << "错误：无法捕捉图像" << std::endl;
        return false;
    }

    if (frame.empty()) {  // 再次检查捕获的图像是否有效
        OUTPUT_ERRR << "错误：捕捉到空的图像" << std::endl;
        return false;
    }

    return true;
}

std::string CAPTURE::get_A_Pic(std::string fileNamesuffix) {
    auto immediate_imwrite = [](const std::string& filename, const cv::Mat& frame) ->bool {
            std::vector<uchar> buffer;
            cv::imencode(".jpg", frame, buffer);
            int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
            write(fd, buffer.data(), buffer.size());
            fsync(fd);  // 正确
            close(fd);
            return true;
        };
    cv::Mat frame;
    // 先grab()快速捕获，再retrieve()解码
    if (!capture.grab()) {
        OUTPUT_ERRR << "错误：无法抓取图像" << std::endl;
        return "";
    }
    
    if (!capture.retrieve(frame)) {
        OUTPUT_ERRR << "错误：无法检索图像" << std::endl;
        return "";
    }
    if (!capture.read(frame)) {  // 检查是否成功捕获图像
        OUTPUT_ERRR << "错误：无法捕捉图像" << std::endl;
        return "";
    }

    if (frame.empty()) {  // 再次检查捕获的图像是否有效
        OUTPUT_ERRR << "错误：捕捉到空的图像" << std::endl;
        return "";
    }

    // 生成文件名并保存图像
    std::string filename = getPicFilePath() + getCurrentTimeString() + "_" + fileNamesuffix + ".jpg";
    if (!immediate_imwrite(filename, frame)) {  // 检查是否成功保存
        OUTPUT_ERRR << "错误：无法保存图像到文件: " << filename << std::endl;
        return "";
    }
    return filename;
}

typedef std::string (CAPTURE::*Callback_get_A_Pic)(std::string);  // 声明回调成员函数指针
class IPC_FIFO {
private:
#ifdef _WIN32
    HANDLE hPipe;
    HANDLE hResponsePipe;
#else
#endif
    Callback_get_A_Pic callback_cap;
    std::shared_ptr<CAPTURE> cap;

public:
    IPC_FIFO(Callback_get_A_Pic callback, std::shared_ptr<CAPTURE> _cap) : callback_cap(callback), cap(_cap) {};
    bool init(void);
    void camera_process();
    bool Ret_pic_info(std::string& ret_pic_path);
    void deinit();
    void handle_tread(void);
    ~IPC_FIFO() {
        OUPUT_INFO << "~IPC_FIFO() and exit()" << std::endl;
        deinit();
    };
};

#include <iostream>
#include <stdexcept>

bool IPC_FIFO::init() {
    OUPUT_INFO << "begin init app!" << std::endl;

#ifdef _WIN32
    // 创建主命名管道
    hPipe = CreateNamedPipeA(
        pipe_path, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 0, 0, 0, NULL);

    if (hPipe == INVALID_HANDLE_VALUE) {
        OUTPUT_ERRR << "Failed to create response named pipe: " << GetLastError() << std::endl;
        CloseHandle(hPipe);  // 释放之前创建的反馈管道
        return false;
    }
#else
    if (access(pipe_path, F_OK) == 0) {
        OUTPUT_ERRR << "Named pipe already exists: " << pipe_path << ", unlink it !" << std::endl;
        unlink(pipe_path);
    }

    // 自动创建命名管道文件（如果不存在）
    if (mkfifo(pipe_path, 0666) == -1) {
        OUTPUT_ERRR << "Failed to create named pipe: " << std::strerror(errno) << std::endl;
        return false;
    }

#endif
    return true;  // 所有管道成功创建
}

void IPC_FIFO::deinit() {
#ifdef _WIN32
    CloseHandle(hPipe);
#else
    if (access(pipe_path, F_OK) == 0) {
        OUPUT_INFO << "Named pipe already still exists: " << pipe_path << ", unlink it !" << std::endl;
        unlink(pipe_path);
    }
#endif
    OUPUT_INFO << "deinit app ok! " << std::endl;
}

bool IPC_FIFO::Ret_pic_info(std::string& ret_pic_path) {
    //    OUPUT_INFO << "Ret_pic_info: " << ret_pic_path << std::endl;
    // #ifdef _WIN32
    //     HANDLE hResponsePipe = CreateFileA(response_pipe_path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    //     if (hResponsePipe != INVALID_HANDLE_VALUE) {
    //         DWORD bytesWritten;
    //         bool success = WriteFile(hResponsePipe, ret_pic_path.c_str(), strlen(ret_pic_path.c_str()),
    //         &bytesWritten, NULL);

    //         if (!success) { // 检查写入是否成功
    //             OUTPUT_ERRR <<  "Failed to write to response pipe: " <<
    //             GetLastError() << std::endl; return false;
    //             // 可以在此处添加其他错误处理逻辑，比如重试或记录日志
    //         } else if (bytesWritten < strlen(ret_pic_path.c_str())) {
    //             OUTPUT_ERRR <<  "Partial write to response pipe. Expected: "
    //                     << strlen(ret_pic_path.c_str()) << ", Written: " << bytesWritten << std::endl;
    //             // 此处也可以进行进一步的错误处理，比如重试或通知
    //         }
    //     } else {
    //         OUTPUT_ERRR <<  "Failed to connect to response pipe after retries.
    //         Error code: " << GetLastError() << std::endl; return false;
    //     }
    // #else
    //     int fd = open(response_pipe_path, O_WRONLY);
    //     if (fd != -1) {
    //         // const char* response = "done";
    //         write(fd, ret_pic_path.c_str(), strlen(ret_pic_path.c_str()));
    //         close(fd);
    //     }
    // #endif
    return true;
}

void IPC_FIFO::camera_process() {
#ifdef _WIN32
    while (running) {
        // 阻塞进程，直到客户端登录
        if (ConnectNamedPipe(hPipe, NULL) ==
            false) {  // 如果出现未能连接到创建的管道的场景(管道已经连接，无效的管道句柄，权限问题)
            OUTPUT_ERRR << "Failed to connect to named pipe: " << GetLastError() << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));  // 等待重试
            continue;                                                     // 继续循环
        }

        char buffer[256];
        DWORD bytesRead;
        if (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL) ==
            false) {  // 阻塞调用，它试图从管道 hPipe 中读取数据。
            OUTPUT_ERRR << "Failed to read from pipe: " << GetLastError() << std::endl;
            DisconnectNamedPipe(hPipe);
            continue;  // 继续循环
        }
        buffer[bytesRead] = '\0';
        std::string get_str = std::string(buffer);
        std::string prefix = "CAP_";
        if (get_str == "exit") {
            break;
        } else if (get_str.rfind("CAP_", 0) == 0) {  // 检查是否以CAP_开头
            // {
            //     std::lock_guard<std::mutex> lock(mtx);
            //     ready = true; // 设置为准备状态
            // }
            // cv_condition.notify_one(); // 唤醒拍照线程
            DWORD bytesWritten;
            get_str.erase(0, prefix.size());  // 删除前缀
            std::string ret_pic_path = cap->get_A_Pic(get_str);
            if (WriteFile(hPipe, ret_pic_path.c_str(), strlen(ret_pic_path.c_str()), &bytesWritten, NULL))  // 写入成功
            {
                OUPUT_INFO << "write succ!" << std::endl;
            } else {
                OUPUT_INFO << "write fail!" << std::endl;  // 写入失败
            }
        } else  // 其他指令
        {
            OUPUT_INFO << "其他指令，无法响应!" << std::endl;
        }
        DisconnectNamedPipe(hPipe);  // 写入完毕了
    }
#else
    while (running) {
        int r_fd = open(pipe_path, O_RDONLY);
        if (r_fd == -1) {  // 命名管道不存在,权限问题,其他系统错误
            OUTPUT_ERRR << "Failed to open named pipe for reading: " << std::strerror(errno) << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));  // 等待重试
            continue;                                                     // 继续循环
        }

        char buffer[256];
        ssize_t bytesRead = read(r_fd, buffer, sizeof(buffer) - 1);
        close(r_fd);

        if (bytesRead == -1) {
            OUTPUT_ERRR << "Failed to read from pipe: " << std::strerror(errno) << std::endl;
            continue;  // 继续循环
        }
        buffer[bytesRead] = '\0';
        std::string get_str = std::string(buffer);
        std::string prefix = "CAP_";
        if (get_str == "exit") {
            OUPUT_INFO << "收到退出任务！" << std::endl;
            break;
        } else if (get_str.rfind("CAP_", 0) == 0) {  // 检查是否以CAP_开头
            // {
            //     std::lock_guard<std::mutex> lock(mtx);
            //     ready = true; // 设置为准备状态
            // }
            // cv_condition.notify_one(); // 唤醒拍照线程
            get_str.erase(0, prefix.size());  // 删除前缀
            std::string ret_pic_path = cap->get_A_Pic(get_str);
            if (ret_pic_path == "") {
                ret_pic_path = "failed";
            }
            int w_fd = open(pipe_path, O_WRONLY);
            if (w_fd != -1) {
                if (write(w_fd, ret_pic_path.c_str(), strlen(ret_pic_path.c_str())) == -1) {
                    OUTPUT_ERRR << "Failed to write to named pipe: " << std::strerror(errno) << std::endl;
                    close(w_fd);  // 关闭写入管道
                } else {
                    OUPUT_INFO << "写入成功 : " << ret_pic_path << std::endl;
                    close(w_fd);  // 关闭写入管道
                }
            }
        } else  // 其他指令
        {
            OUPUT_INFO << "其他指令，无法响应!" << std::endl;
        }
    }
#endif
}

void IPC_FIFO::handle_tread() {
    // OUPUT_INFO << "begin handle take photo tread!"<< std::endl;
    //  while (running) {
    //      std::unique_lock<std::mutex> lock(mtx); // 获取锁的状态
    //      cv_condition.wait(lock, [] { return ready.load(); });
    //      // 自动释放lock， 当线程调用 wait 时，它会释放该锁并进入等待状态，
    //      // 直到其他线程调用 notify_one() 或 notify_all() 来唤醒它。
    //      // 此时，它会重新获取锁后继续执行。因此，等待直到ready为true

    //     if (!running) break; // 检查是否停止运行
    //     std::string ret;
    //     try {
    //         ret = capture_photo(); // 拍照逻辑
    //         if (Ret_pic_info(ret)) // 回调，写回读取结果
    //         {
    //            OUPUT_INFO << "return Take photo ok !"<< std::endl;
    //         }
    //     } catch (const std::exception& e) {
    //         OUTPUT_ERRR << "Error capturing photo: " << e.what() << std::endl;
    //     }
    //     ready = false; // 拍照完成后重置状态
    // }
}

int main(int argc, char** argv) {
#ifdef _WIN32
    SetConsoleCtrlHandler(ConsoleHandler, true);  // 捕获 Ctrl+C 和关闭事件
#else
    signal(SIGINT, signal_handler);  // 捕获 Ctrl+C 信号
#endif
    std::shared_ptr<CAPTURE> cap = std::make_shared<CAPTURE>("/dev/video0");  // 给一个路径
    if (!cap->init()) {
        OUPUT_INFO << "CAPTURE init Failed!" << std::endl;
        return 1;
    } else {
        OUPUT_INFO << "CAPTURE init ok!" << std::endl;
    }

    IPC_FIFO fifo_comm(&CAPTURE::get_A_Pic, cap);
    if (!fifo_comm.init())  // 初始化不成功
    {
        OUPUT_INFO << "fifo_comm init Failed!" << std::endl;
        return 1;
    } else {
        OUPUT_INFO << "fifo_comm init ok!" << std::endl;
    }
    // 可以使用 std::ref 来强制以引用方式传递 fifo_comm，避免对象拷贝：这样 worker 线程将会共享 fifo_comm
    // 的引用，而不是拷贝对象，从而避免析构的早期调用问题。 std::thread worker(&IPC_FIFO::handle_tread,
    // std::ref(fifo_comm)); // 启动拍照线程
    fifo_comm.camera_process();  // 启动相机进程
    // worker.join(); // 等待拍照线程完成
    return 0;
}
